package org.aplikator.server.persistence.empiredb.oracle;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.empire.db.DBCmdType;
import org.apache.empire.db.DBColumn;
import org.apache.empire.db.DBDatabase;
import org.apache.empire.db.DBExpr;
import org.apache.empire.db.DBIndex;
import org.apache.empire.db.DBObject;
import org.apache.empire.db.DBRelation;
import org.apache.empire.db.DBSQLScript;
import org.apache.empire.db.DBTable;
import org.apache.empire.db.DBTableColumn;
import org.apache.empire.db.DBView;
import org.apache.empire.db.exceptions.EmpireSQLException;
import org.apache.empire.db.oracle.OracleDDLGenerator;
import org.apache.empire.exceptions.InvalidArgumentException;
import org.apache.empire.exceptions.NotImplementedException;
import org.apache.empire.exceptions.NotSupportedException;
import org.aplikator.server.persistence.PersisterFactory;

public class EmpireDDLGeneratorOracle extends OracleDDLGenerator {
	public EmpireDDLGeneratorOracle(EmpireDriverOracle driver) {
		super(driver);
	}

	@Override
	protected void appendColumnDesc(DBTableColumn c, boolean alter,
			StringBuilder sql) {
		if (alter) {
			// Append name
			c.addSQL(sql, DBExpr.CTX_NAME);
			sql.append(" ");
			// Unknown data type
			if (!appendColumnDataType(c.getDataType(), c.getSize(), c, sql))
				return;
			// Default Value
			if (driver.isDDLColumnDefaults() && !c.isAutoGenerated()
					&& c.getDefaultValue() != null) {
				sql.append(" DEFAULT ");
				sql.append(driver.getValueString(c.getDefaultValue(),
						c.getDataType()));
			}
		} else {
			super.appendColumnDesc(c, alter, sql);
		}
	}

	@Override
	protected void createSequence(DBDatabase db, DBTableColumn c,
			DBSQLScript script) {
		super.createSequence(db, c, script);
	}

	protected void dropSequence(String sequenceName, DBSQLScript script) {
		String sql = "drop sequence \"" + sequenceName + "\"";
		script.addStmt(sql);
	}

	public void getDDLDropRelation(String tableName, String relationName,
			DBSQLScript script) {
		String sql = "ALTER TABLE " + tableName + " DROP CONSTRAINT "
				+ relationName;
		script.addStmt(sql);
	}

	private void getDDLPrimaryKey(DBIndex pk, DBSQLScript script, DBCmdType type) {
		StringBuilder sql = new StringBuilder();
		boolean addSeparator;

		if (DBCmdType.DROP.equals(type)) {
			sql.append("ALTER TABLE " + pk.getTable().getName()
					+ " DROP PRIMARY KEY");
			script.addStmt(sql);
		}

		if (DBCmdType.CREATE.equals(type)) {
			if (pk != null) { // add the primary key
				sql.append("ALTER TABLE " + pk.getTable().getName() + " ADD");
				if (namePrimaryKeyConstraint) {
					sql.append(" CONSTRAINT ");
					appendElementName(sql, pk.getName());
				}
				sql.append(" PRIMARY KEY (");
				addSeparator = false;
				// columns
				DBColumn[] keyColumns = pk.getColumns();
				for (int i = 0; i < keyColumns.length; i++) {
					sql.append((addSeparator) ? ", " : "");
					keyColumns[i].addSQL(sql, DBExpr.CTX_NAME);
					addSeparator = true;
				}
				sql.append(")");
			}
			script.addStmt(sql);
		}
	}

	// GetDDL
	public void getDDLScript(DBCmdType type, DBObject dbo, DBSQLScript script) {

		if (dbo == null)
			throw new InvalidArgumentException("dbo", dbo);

		// The Object's database must be attached to this driver
		if (!DBCmdType.DROP.equals(type))
			if (dbo.getDatabase().getDriver() != driver)
				throw new InvalidArgumentException("dbo", dbo);
		
		// Check Type of object
		if (dbo instanceof DBDatabase) { // Database
			switch (type) {
			case CREATE:
				createDatabase((DBDatabase) dbo, script);
				return;
			case DROP:
				dropObject(((DBDatabase) dbo).getSchema(), "USER", script);
				return;
			case ALTER:
				if (((DBDatabase)dbo).getDriver()!=null) {
					EmpireDriverOracle driver = (EmpireDriverOracle) ((DBDatabase)dbo).getDriver();
					try {
						Connection conn = PersisterFactory.getPersister().getJDBCConnection(); 
							((EmpireDriverOracle) driver).checkDatabase((DBDatabase) dbo, conn.getMetaData().getUserName(), conn,script);
						} catch (SQLException ex) {
							throw new EmpireSQLException(driver,ex);
						}
					}
				return;
			default:
				throw new NotImplementedException(this, "getDDLScript."
						+ dbo.getClass().getName() + "." + type);
			}
		} else if (dbo instanceof DBTable) { // Table
			switch (type) {
			case CREATE:
				createTable((DBTable) dbo, script);
				return;
			case DROP:
				dropObject(((DBTable) dbo).getName(), "TABLE", script);
				return;
			default:
				throw new NotImplementedException(this, "getDDLScript."
						+ dbo.getClass().getName() + "." + type);
			}
		} else if (dbo instanceof DBView) { // View
			switch (type) {
			case CREATE:
				createView((DBView) dbo, script);
				return;
			case DROP:
				dropObject(((DBView) dbo).getName(), "VIEW", script);
				return;
			case ALTER:
				dropObject(((DBView) dbo).getName(), "VIEW", script);
				createView((DBView) dbo, script);
				return;
			default:
				throw new NotImplementedException(this, "getDDLScript."
						+ dbo.getClass().getName() + "." + type);
			}
		} else if (dbo instanceof DBRelation) { // Relation
			switch (type) {
			case CREATE:
				createRelation((DBRelation) dbo, script);
				return;
			case DROP:
				getDDLDropRelation(((DBRelation) dbo).getReferences()[0]
						.getSourceColumn().getRowSet().getName(),
						((DBRelation) dbo).getName(), script);
				return;
			case ALTER:
				getDDLDropRelation(((DBRelation) dbo).getReferences()[0]
						.getSourceColumn().getRowSet().getName(),
						((DBRelation) dbo).getName(), script);
				createRelation((DBRelation) dbo, script);
				return;
			default:
				throw new NotImplementedException(this, "getDDLScript."
						+ dbo.getClass().getName() + "." + type);
			}
		} else if (dbo instanceof DBIndex) { // Relation
			switch (type) {
			case CREATE:
				if (((DBIndex) dbo).getType() == DBIndex.PRIMARYKEY) {
					getDDLPrimaryKey((DBIndex) dbo, script, type);
				} else {
					createIndex(((DBIndex) dbo).getTable(), (DBIndex) dbo,
							script);
				}
				return;
			case DROP:
				if (((DBIndex) dbo).getType() == DBIndex.PRIMARYKEY) {
					getDDLPrimaryKey((DBIndex) dbo, script, DBCmdType.DROP);
				} else {
					dropObject(((DBIndex) dbo).getName(), "INDEX", script);
				}
				return;
			case ALTER:
				if (((DBIndex) dbo).getType() == DBIndex.PRIMARYKEY) {
					getDDLPrimaryKey((DBIndex) dbo, script, DBCmdType.DROP);
					getDDLPrimaryKey((DBIndex) dbo, script, DBCmdType.CREATE);
				} else {
					dropObject(((DBIndex) dbo).getName(), "INDEX", script);
					createIndex(((DBIndex) dbo).getTable(), (DBIndex) dbo,
							script);
				}
				return;
			default:
				throw new NotImplementedException(this, "getDDLScript."
						+ dbo.getClass().getName() + "." + type);
			}
		} else if (dbo instanceof DBTableColumn) { // Table Column
			alterTable((DBTableColumn) dbo, type, script);
			return;
		} else { // dll generation not supported for this type
			throw new NotSupportedException(this, "getDDLScript() for "
					+ dbo.getClass().getName());
		}
	}

	public void changeNullabilityOnly(DBTableColumn c, DBSQLScript script) {
		StringBuilder sql = new StringBuilder();
		sql.append("ALTER TABLE ");
		c.getRowSet().addSQL(sql, DBExpr.CTX_FULLNAME);
		sql.append(alterColumnPhrase);
		sql.append(c.getName());
		if (c.isAutoGenerated() || c.isRequired()) {
			sql.append(" NOT NULL");
		} else {
			sql.append(" NULL");
		}
		script.addStmt(sql);
	}

}
